import {
  Controller,
  Post,
  Body,
  Get,
  Param,
  Query,
  BadRequestException,
  Logger,
  UseGuards,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiParam,
  ApiQuery,
  ApiBearerAuth,
} from '@nestjs/swagger';
import { ObsService } from '../services/obs.service';
import { AttachmentConfirmDto } from '../dto/attachment-confirm.dto';
import { AttachmentResponseDto } from '../dto/attachment-response.dto';
import { UploadUrlResponseDto } from '../dto/upload-url-response.dto';
import { DownloadUrlResponseDto } from '../dto/download-url-response.dto';
import { BatchUploadUrlRequestDto } from '../dto/batch-upload-url-request.dto';
import { BatchUploadUrlResponseDto } from '../dto/batch-upload-url-response.dto';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { AttachmentsService } from '../services/attachments.service';

@ApiTags('Attachment')
@Controller('attachments')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
export class AttachmentsController {
  private readonly logger = new Logger(AttachmentsController.name);

  constructor(
    private readonly obsService: ObsService,
    private readonly attachmentsService: AttachmentsService,
  ) {}

  @Get('upload-url')
  @ApiOperation({ summary: 'Generate a pre-signed URL for file upload' })
  @ApiQuery({
    name: 'resourceName',
    required: true,
    description: 'Name of the resource being uploaded',
  })
  @ApiResponse({
    status: 200,
    description: 'Pre-signed URL generated successfully',
    type: UploadUrlResponseDto,
  })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async generateUploadUrl(
    @Query('resourceName') resourceName: string,
  ): Promise<UploadUrlResponseDto> {
    try {
      if (!resourceName) {
        throw new BadRequestException('Resource name is required');
      }

      this.logger.log(`Generating upload URL for resource: ${resourceName}`);

      const {
        url,
        expiresIn: expiration,
        headers,
      } = await this.obsService.generateUploadUrl(resourceName);

      return {
        uploadUrl: url,
        expiresIn: expiration,
        headers,
      };
    } catch (error) {
      this.logger.error(`Failed to generate upload URL: ${error.message}`, error.stack);
      throw new BadRequestException('Failed to generate upload URL');
    }
  }

  @Post('confirm')
  @ApiOperation({ summary: 'Confirm file upload and save attachment record' })
  @ApiResponse({
    status: 200,
    description: 'Attachment record saved successfully',
    type: AttachmentResponseDto,
  })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async confirmAttachment(
    @Body() attachmentConfirmDto: AttachmentConfirmDto,
  ): Promise<AttachmentResponseDto> {
    try {
      // Validate the file URL
      if (!this.obsService.validateFileUrl(attachmentConfirmDto.fileUrl)) {
        throw new BadRequestException('Invalid file URL');
      }

      this.logger.log(`Confirming attachment upload: ${attachmentConfirmDto.filename}`);

      // Save the attachment record
      const attachment = await this.attachmentsService.createAttachment(attachmentConfirmDto);

      return attachment;
    } catch (error) {
      this.logger.error(`Failed to confirm attachment: ${error.message}`, error.stack);
      throw new BadRequestException(error.message || 'Failed to confirm attachment');
    }
  }

  @Get(':attachmentId/download-url')
  @ApiOperation({ summary: 'Generate a pre-signed URL for file download' })
  @ApiParam({
    name: 'attachmentId',
    description: 'ID of the attachment to download',
    type: String,
    format: 'uuid',
  })
  @ApiQuery({
    name: 'expiresIn',
    required: false,
    description: 'Expiration time in seconds (default: 3600)',
  })
  @ApiResponse({
    status: 200,
    description: 'Pre-signed URL generated successfully',
    type: DownloadUrlResponseDto,
  })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  @ApiResponse({ status: 404, description: 'Attachment not found' })
  async generateDownloadUrl(
    @Param('attachmentId') attachmentId: string,
    @Query('expiresIn') expiresIn?: number,
  ): Promise<DownloadUrlResponseDto> {
    try {
      // Get the attachment record
      const attachment = await this.attachmentsService.findById(attachmentId);

      if (!attachment) {
        throw new BadRequestException('Attachment not found');
      }

      // Extract the object key from the file URL
      const objectKey = this.obsService.extractObjectKeyFromUrl(attachment.fileUrl);

      this.logger.log(`Generating download URL for attachment: ${attachmentId}`);

      const { url, expiresIn: expiration } = await this.obsService.generateDownloadUrl(
        objectKey,
        expiresIn,
      );

      return {
        downloadUrl: url,
        expiresIn: expiration,
      };
    } catch (error) {
      this.logger.error(`Failed to generate download URL: ${error.message}`, error.stack);
      throw new BadRequestException(error.message || 'Failed to generate download URL');
    }
  }

  @Post('batch-upload-urls')
  @ApiOperation({ summary: 'Generate multiple pre-signed URLs for file uploads in batch' })
  @ApiResponse({
    status: 200,
    description: 'Pre-signed URLs generated successfully',
    type: BatchUploadUrlResponseDto,
  })
  @ApiResponse({ status: 400, description: 'Bad request' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async generateBatchUploadUrls(
    @Body() batchUploadUrlRequestDto: BatchUploadUrlRequestDto,
    @Query('expiresIn') expiresIn?: number,
  ): Promise<BatchUploadUrlResponseDto> {
    try {
      if (!batchUploadUrlRequestDto.resources || batchUploadUrlRequestDto.resources.length === 0) {
        throw new BadRequestException('At least one resource name is required');
      }

      this.logger.log(
        `Generating batch upload URLs for ${batchUploadUrlRequestDto.resources.length} resources`,
      );

      const resourceNames = batchUploadUrlRequestDto.resources.map(
        (resource) => resource.resourceName,
      );
      const uploadUrlsData = await this.obsService.generateBatchUploadUrls(
        resourceNames,
        expiresIn,
      );

      // Transform the response to match the expected format
      const uploadUrls = uploadUrlsData.map(({ url, expiresIn, resourceName, headers }) => ({
        uploadUrl: url,
        expiresIn,
        resourceName,
        headers,
      }));

      return { uploadUrls };
    } catch (error) {
      this.logger.error(`Failed to generate batch upload URLs: ${error.message}`, error.stack);
      throw new BadRequestException(error.message || 'Failed to generate batch upload URLs');
    }
  }
}
